package cn.maihe.elg.operation.supports.bank;

import cn.maihe.elg.operation.config.CiticBankConfig;
import cn.maihe.elg.operation.model.dto.BaseReq;
import cn.maihe.elg.operation.model.dto.BaseResp;
import cn.maihe.elg.operation.supports.bank.dto.CiticAccountDetailReqDTO;
import cn.maihe.elg.operation.supports.bank.dto.CiticAccountDetailRespDTO;
import cn.maihe.elg.operation.supports.bank.dto.CiticBaseRespDTO;
import cn.maihe.elg.operation.utils.ElgLogger;
import cn.maihe.elg.operation.utils.IdGenerate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 @description: 中信银行银企直连基础服务类
 */
@Service
@Slf4j
public abstract class BaseCiticBankTransService<REQ extends BaseReq, RESP extends BaseResp> {

    protected Class<REQ> reqClass;

    protected Class<RESP> respClass;

    @Resource
    protected CiticBankConfig citicBankConfig;

    @Resource(name = "citicRestTemplate")
    protected RestTemplate restTemplate;

    public BaseCiticBankTransService() {
        Type[] types = ((ParameterizedType) (getClass().getGenericSuperclass())).getActualTypeArguments();
        this.reqClass = (Class<REQ>) types[0];
        this.respClass = (Class<RESP>) types[1];
    }

    @PostConstruct
    public void init() {
        ElgLogger.info(log, l -> l.info("中信银行参数初始化完成"));
    }

    /**
     发送中信银行账户明细查询请求
     */
    public CiticAccountDetailRespDTO sendCiticAccountDetailQuery(CiticAccountDetailReqDTO req) {
        String traceId = IdGenerate.genTranceId();
        try {
            validateRequest(req);
            String requestXml = buildRequestXml(req, CiticAccountDetailReqDTO.class);
            ElgLogger.info(log, l -> l.info("{} >> 中信银行账户明细查询 请求报文:{}", traceId, requestXml));
            String responseXml = sendHttpRequest(requestXml);
            ElgLogger.info(log, l -> l.info("{} << 中信银行账户明细查询 响应报文:{}", traceId, responseXml));
            CiticAccountDetailRespDTO response = parseResponseXml(responseXml, CiticAccountDetailRespDTO.class);
            validateBaseResponse(response);
            return response;
        } catch (Exception e) {
            ElgLogger.error(log, l -> l.error("{} << 中信银行账户明细查询 请求响应异常:{}", traceId, e.getMessage(), e));
            throw new RuntimeException("中信银行账户明细查询异常:" + e.getMessage());
        }
    }

    /**
     参数校验
     */
    private void validateRequest(CiticAccountDetailReqDTO req) {
        Assert.hasText(req.getUserName(), "登录名不能为空");
        Assert.hasText(req.getAccountNo(), "账号不能为空");
        Assert.hasText(req.getStartDate(), "起始日期不能为空");
        Assert.hasText(req.getEndDate(), "终止日期不能为空");
        Assert.notNull(req.getPageNumber(), "请求记录条数不能为空");
        Assert.isTrue(req.getPageNumber() <= 20, "请求记录条数不能超过20");
    }

    /**
     构建XML请求报文
     */
    protected <T> String buildRequestXml(T reqObject, Class<T> clazz) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(clazz);
        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_ENCODING, "GBK");
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        StringWriter writer = new StringWriter();
        writer.write("<?xml version=\"1.0\" encoding=\"GBK\"?>\n");
        marshaller.marshal(reqObject, writer);
        return writer.toString();
    }
    /**
     发送HTTP请求
     */
    protected String sendHttpRequest(String requestXml) {
        try {
            org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
            headers.set("Content-Type", "application/xml; charset=GBK");
            byte[] requestBytes = requestXml.getBytes("GBK");
            org.springframework.http.HttpEntity<byte[]> entity = new org.springframework.http.HttpEntity<>(requestBytes, headers);
            org.springframework.http.ResponseEntity<byte[]> response = restTemplate.postForEntity(
                    citicBankConfig.getRequestUrl(),
                    entity,
                    byte[].class
            );
            if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
                return new String(response.getBody(), "GBK");
            } else {
                throw new RuntimeException("HTTP请求失败，状态码: " + response.getStatusCode());
            }
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("GBK编码不支持", e);
        } catch (Exception e) {
            ElgLogger.error(log, l -> l.error("发送HTTP请求到中信银行时发生异常", e));
            throw new RuntimeException("HTTP请求异常: " + e.getMessage(), e);
        }
    }
    protected <T> T parseResponseXml(String responseXml, Class<T> clazz) throws JAXBException {
        if (responseXml == null || responseXml.trim().isEmpty()) {
            throw new IllegalArgumentException("银行返回的响应报文为空");
        }
        responseXml = responseXml.trim();
        JAXBContext context = JAXBContext.newInstance(clazz);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        StringReader reader = new StringReader(responseXml);
        return (T) unmarshaller.unmarshal(reader);
    }
    protected void validateBaseResponse(CiticBaseRespDTO response) {
        Assert.notNull(response, "响应报文解析结果为空");
        Assert.hasText(response.getStatus(), "响应报文中交易状态(status)为空");
        if (!"AAAAAAA".equals(response.getStatus())) {
            ElgLogger.warn(log, l -> l.warn("中信银行交易通信层面未成功，状态码: {}, 状态信息: {}", response.getStatus(), response.getStatusText()));
            if (response.getStatus() != null && (response.getStatus().startsWith("E") || response.getStatus().startsWith("F") || "UNKNOWN".equals(response.getStatus()))) {
                throw new RuntimeException("银行交易失败，状态码: " + response.getStatus() + ", 状态信息: " + response.getStatusText());
            }
        }
    }
}